package com.lixh.sdk.base.http

import android.text.TextUtils
import android.util.Log
import okhttp3.*
import okio.Buffer
import java.io.IOException
import java.util.*
import java.util.concurrent.TimeUnit

/**
 * Created by zhy on 16/3/1.
 */
class LoggerInterceptor @JvmOverloads constructor(tag: String?, showResponse: Boolean = false) :
    Interceptor {
    private val tag: String?
    private val showResponse: Boolean = showResponse

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request: Request = chain.request()
        logForRequest(request)
        val startNs = System.nanoTime()
        val response: Response = chain.proceed(request)
        val tookMs: Long = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
        print("responseBody's waitTime : $tookMs")
        return logForResponse(response)
    }

    private fun logForResponse(response: Response): Response {
        try {
            //===>response log
            Log.e(tag, "========response'log=======")
            val builder: Response.Builder = response.newBuilder()
            val clone: Response = builder.build()
            Log.e(tag, "url : " + clone.request.url)
            Log.e(tag, "code : " + clone.code)
            Log.e(tag, "protocol : " + clone.protocol)
            if (!TextUtils.isEmpty(clone.message)) Log.e(tag, "message : " + clone.message)

            if (showResponse) {
                var body = clone.body
                if (body != null) {
                    val mediaType = body.contentType()
                    if (mediaType != null) {
                        Log.e(tag, "responseBody's contentType : $mediaType")
                        if (isText(mediaType)) {
                            val resp = body.string()
                            print("responseBody's content : $resp")
                            body = ResponseBody.create(mediaType, resp)
                            return response.newBuilder().body(body).build()
                        } else {
                            Log.e(
                                tag,
                                "responseBody's content : " + " maybe [file part] , too large too print , ignored!"
                            )
                        }
                    }
                }
            }
            Log.e(tag, "========response'log=======end")
        } catch (e: Exception) {
//            e.printStackTrace();
        }
        return response
    }

    /**
     * 打印日志到控制台（解决Android控制台丢失长日志记录）
     *
     * @param content
     */
    fun print(content: String) {
        // 1. 测试控制台最多打印4062个字节，不同情况稍有出入（注意：这里是字节，不是字符！！）
        // 2. 字符串默认字符集编码是utf-8，它是变长编码一个字符用1~4个字节表示
        // 3. 这里字符长度小于1000，即字节长度小于4000，则直接打印，避免执行后续流程，提高性能哈
        if (content.length < 1000) {
            Log.e(TAG, content)
            return
        }

        // 一次打印的最大字节数
        val maxByteNum = 4000

        // 字符串转字节数组
        var bytes = content.toByteArray()

        // 超出范围直接打印
        if (maxByteNum >= bytes.size) {
            Log.e(TAG, content)
            return
        }

        // 分段打印计数
        var count = 1

        // 在数组范围内，则循环分段
        while (maxByteNum < bytes.size) {
            // 按字节长度截取字符串
            val subStr = cutStr(bytes, maxByteNum)

            // 打印日志
            val desc = String.format("分段打印(%s):%s", count++, subStr)
            Log.e(TAG, desc)

            // 截取出尚未打印字节数组
            bytes = bytes.copyOfRange(subStr!!.toByteArray().size, bytes.size)

        }
        // 打印剩余部分
        Log.e(TAG, String.format("分段打印(%s):%s", count, String(bytes)))
    }


    /**
     * 按字节长度截取字节数组为字符串
     *
     * @param bytes
     * @param subLength
     * @return
     */
    fun cutStr(bytes: ByteArray?, subLength: Int): String? {
        // 边界判断
        if (bytes == null || subLength < 1) {
            return null
        }

        // 超出范围直接返回
        if (subLength >= bytes.size) {
            return String(bytes)
        }

        // 复制出定长字节数组，转为字符串
        val subStr: String = String(Arrays.copyOf(bytes, subLength))

        // 避免末尾字符是被拆分的，这里减1使字符串保持完整
        return subStr.substring(0, subStr.length - 1)
    }

    private fun logForRequest(request: Request) {
        try {
            val url = request.url.toString()
            val headers = request.headers
            Log.e(tag, "========request'log=======")
            Log.e(tag, "method : " + request.method)
            Log.e(tag, "url : $url")
            if (headers.size > 0) {
                Log.e(tag, "headers : $headers")
            }
            val requestBody = request.body
            if (requestBody != null) {
                val mediaType = requestBody.contentType()
                if (mediaType != null) {
                    Log.e(tag, "requestBody's contentType : $mediaType")
                    if (isText(mediaType)) {
                        Log.e(tag, "requestBody's content : " + bodyToString(request))
                    } else {
                        Log.e(
                            tag,
                            "requestBody's content : " + " maybe [file part] , too large too print , ignored!"
                        )
                    }
                }
            }
            Log.e(tag, "========request'log=======end")
        } catch (e: Exception) {
//            e.printStackTrace();
        }
    }

    private fun isText(mediaType: MediaType): Boolean {
        if (mediaType.type == "text") {
            return true
        }
        if (mediaType.subtype == "json" || mediaType.subtype == "x-www-form-urlencoded" || mediaType.subtype == "xml" || mediaType.subtype == "html" || mediaType.subtype == "webviewhtml") return true
        return false
    }

    private fun bodyToString(request: Request): String {
        return try {
            val copy = request.newBuilder().build()
            val buffer = Buffer()
            copy.body!!.writeTo(buffer)
            buffer.readUtf8()
        } catch (e: IOException) {
            "something error when show requestBody."
        }
    }

    companion object {
        const val TAG = "OkHttpUtils"
    }

    init {
        this.tag = tag ?: TAG
    }
}